// XferFIle.cs
//
//  Copyright (C) 2008-2009 Christian Eide
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//

using System;
using System.Collections.Generic;

namespace Sarnata.Net.BareFtp.Protocol
{
	
	public enum DownloadStatus 	{
		Queued = 0,
		Downloading,
		Uploading,
		Skipped,
		Aborted,
		Finished,
		Failed,
		Undefined
	}
	
	public enum TransferDirection {
		Download = 0,
		Upload,
		Undefined
	}
	
	public enum FileAction : int {
		Overwrite = 0,
		Resume,
		Append,
		Skip,
		Undefined
	}
	
	public class XferFile {
		private string remote_server;
		private long total_bytes;
		private long transfered_bytes;
		private long fileId;
		private DownloadStatus status;
		private FileAction action;
		private TransferDirection tr_direction;
		private DateTime start_time;
		private DateTime last_speedcheck_time;
		private DateTime end_time;
		private long bytes_since_last_speed_check;
		private TransferProgressEmittedArgs pargs;
		private bool isdir;
		private long marker = 0;
		private int eta = 0;
		private double transfer_rate = 0;
		private string permissions;
		private System.Threading.Timer timer;
		private XferFile parent;
		private List<XferFile> children = null;
		private BarePath path;
		
		public event EventHandler TransferProgressEmitted;
		public virtual void OnTransferProgressEmitted(TransferProgressEmittedArgs e) {
			if(TransferProgressEmitted != null)
				TransferProgressEmitted(this, e);
		}
		
		public event EventHandler TransferInitiated;
		public virtual void OnTransferInitiated(TransferProgressEmittedArgs e) {
			if(TransferInitiated != null)
				TransferInitiated(this, e);
		}
		
		public event EventHandler TransferPaused;
		public virtual void OnTransferPaused(TransferProgressEmittedArgs e) {
			if(TransferPaused != null)
				TransferPaused(this, e);
		}
		
		public event EventHandler TransferFinished;
		public virtual void OnTransferFinished(TransferProgressEmittedArgs e) {
			if(TransferFinished != null)
				TransferFinished(this, e);
		}
		
		public XferFile(long FileId, BarePath rPath, 
		                long size, TransferDirection direction, bool isdir, string remote_server)	
		{
			
			path = rPath;
			this.remote_server = remote_server;
			timer = new System.Threading.Timer(ProgressStatus, null, System.Threading.Timeout.Infinite, 800);
			
			fileId = FileId;
			pargs = new TransferProgressEmittedArgs(this);
			this.isdir = isdir;
			this.action = FileAction.Undefined;
			// Filename is relative to the current working directory on local or remote site
			// TODO: filename seems to be absolute path
			
			
			// TODO: Move size def out of this constructor
			if(direction == TransferDirection.Upload && !isdir) {
				System.IO.FileInfo fi = new System.IO.FileInfo(System.IO.Path.Combine(path.LocalPath, path.FileName));
				this.total_bytes = fi.Length;
			}
			else {
				this.total_bytes = size;
			}
			
			transfered_bytes = 0;
			if(fileId == -1)
			{
				status = DownloadStatus.Undefined;
				children = new List<XferFile>();
			}
			else
				status = DownloadStatus.Queued;
			
			tr_direction = direction;
			bytes_since_last_speed_check = 0;
			
		}
		
		public long FileId {
			get { return fileId; }
		}
		
		public string RemoteServer {
			get { return remote_server; }
		}
		
		public XferFile Parent {
			get { return parent; }
			set { parent = value;}
		}
		
		public List<XferFile> Children {
			get { return children; }
		}
		
		public long Marker {
			get { return marker; }
			set { 
				marker = value;
			}
		}
		
		public FileAction Action {
			get { return action; }
			set { action = value; }
		}
				
		public bool IsDir {
			get { return isdir; }
		}

		public BarePath Path {
			get {
				return this.path;
			}
			set {
				this.path = value;
			}
		}
		
		public long Size
		{
			get { return TotalBytes; }
		}
		
		public long TotalBytes {
			get	
			{
				if(fileId>-1)
					return total_bytes;
				else
				{
					long sum = 0;
					foreach(XferFile f in children)
						sum += f.TotalBytes;
					return sum;
				}
			}
			set
			{
				total_bytes = value;	
			}
		}
				
		public TransferDirection Direction {
			get {
				return tr_direction;
			}
			set {
				tr_direction = value;
			}
		}
		
		public DownloadStatus Status
		{
			get 
			{ 
				return status;
			}
			set 
			{
				// TODO: Clean up this one...
				if(status == DownloadStatus.Queued || status == DownloadStatus.Undefined || status == DownloadStatus.Finished)
				{
					if(value == DownloadStatus.Downloading || value == DownloadStatus.Uploading) {
						this.start_time = DateTime.Now;
						status = value;
						if(fileId >= 0)
						{
							OnTransferInitiated(pargs);
							if(timer == null)
							timer = new System.Threading.Timer(ProgressStatus, null, 0, 800);
							timer.Change(0, 800);
						}
					}
					else
						status = value; 
				}
				
				if(value == DownloadStatus.Finished) {
					
					bool finished = true;
					
					if(children != null)
					{
						foreach(XferFile f in this.children)
						{
							if(f.Status != DownloadStatus.Finished)
							{
								finished = false;
							}
						}
					}
					
					if(finished)
					{
						if(timer != null)
						{
							timer.Change(System.Threading.Timeout.Infinite, 1000);
							timer.Dispose();
							timer = null;
						}
						total_bytes = TransferedBytes;
						this.end_time = DateTime.Now;
						status = value;
						if(fileId >= 0)
						{
							OnTransferFinished(pargs);
						}
						
					}
				}
				if(value == DownloadStatus.Aborted)
				{
					status = value;
					if(fileId >= 0)
					{
						OnTransferFinished(pargs);
					}
					if(timer != null)
					{
						timer.Dispose();
						timer = null;
					}
				}
				if(value == DownloadStatus.Failed)
				{
					// TODO: Do something to make the progress list lokk better when failed
				}
				if(parent != null)
						parent.Status = value;
			}
		}
		
		public double Complete {
			get {
				
				try {
						double result = (double)TransferedBytes/((double)TotalBytes);
						if(result > 0)
							return result;
						else
							return 0;
				}
				catch {
					return (double)0;
				}
			}
		}
		
		public void ProgressStatus(Object state) 
		{	
			
			double secs = (DateTime.Now.Subtract(last_speedcheck_time)).TotalSeconds;
			transfer_rate = ((double)bytes_since_last_speed_check/secs);
			eta = (int)((total_bytes - transfered_bytes) / transfer_rate);
			bytes_since_last_speed_check = 0;
			last_speedcheck_time = DateTime.Now;
			
			OnTransferProgressEmitted(pargs);
		}

		public long TransferedBytes {
			get {
				if(children == null)
				{
					if(transfered_bytes >= 0)
						return transfered_bytes;
				}
				else
				{
					long sum = 0;
					foreach(XferFile f in children)
						sum += f.TransferedBytes;
					return sum;
				}
				return 0;
			}
			set 
			{ 
				bytes_since_last_speed_check += (value - transfered_bytes); 
				transfered_bytes = value;
			}
			
		}
		
		public int ETA
		{
			get { return eta; }
		}
		
		public double TransferRate
		{
			get { return transfer_rate; }
		}
		
		
		public DateTime StartTime {
			get { return this.start_time; }
		}
		
		public DateTime EndTime	{
			get { return this.end_time; }
		}

		public string Permissions {
			get {
				return permissions;
			}
			set {
				permissions = value;
			}
		}
		
	}
}
